package com.atao.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.atao.constants.SystemConstant;
import com.atao.domain.Result;
import com.atao.domain.entity.Music;
import com.atao.domain.entity.MusicComment;
import com.atao.domain.vo.MusicLogVo;
import com.atao.domain.vo.MusicUrlVo;
import com.atao.service.MusicService;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class MusicServiceImpl implements MusicService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    static private final Map<String, String> headersMap;
    final static String[] headers = new String[]{
            "Accept", "application/json, text/plain, */*",
            "Accept - Encoding", "gzip, deflate",
            "Accept-Language", "zh-CN,zh;q=0.9",
            "Connection", "keep-alive",
            "csrf", "J6UKIKREG",
            "Cookie", "_ga=GA1.2.2065300548.1640428004; _gid=GA1.2.758308048.1640428004; gtoken=JBJAbTTZw0dB; gid=b1054c0d-2405-4a66-af33-d58e9422ad74; JSESSIONID=17424rcckc6gd1x5aronml5v6t; Hm_lvt_cdb524f42f0ce19b169a8071123a4797=1640431640,1640431834,1640431891,1640434012; Hm_lpvt_cdb524f42f0ce19b169a8071123a4797=1640434012; kw_token=J6UKIKREG",
            "Host", "www.kuwo.cn",
            "Referer", "http://www.kuwo.cn",
            "User-Agent", "Mozilla/5.0",
    };
    static {
        headersMap = new HashMap<>();
        for (int i = 0; i < headers.length; i += 2) {
            headersMap.put(headers[i], headers[i + 1]);
        }
    }

    /**
     * 根据关键字查询歌曲列表
     */
    @Override
    public Result getMusic(String keyword) {
        if (!StringUtils.hasText(keyword)) {
            return Result.fail("关键字为空！");
        }
        //地址：
        String url =
                "http://www.kuwo.cn/api/www/search/searchMusicBykeyWord?key="
                        + keyword +
                        "&pn=1&rn=30&httpsStatus=1";
        try {
            Connection tempConn = Jsoup.connect(url);
            //模拟浏览器的请求头
            tempConn.headers(headersMap);
            //开始连接HTTP请求。
            Connection.Response demo = tempConn.ignoreContentType(true).method(Connection.Method.GET)
                    .execute();
            Document documentDemo = demo.parse();
            //这里就是获取该页面的HTML元素。
            String data = documentDemo.getElementsByTag("body").toString();
            Map<String, Object> map = JSONUtil.toBean(data.substring(7, data.length() - 7), Map.class);
            //设置reqid
            redisTemplate.opsForValue().set(SystemConstant.REQ_ID, (String) map.get("reqId"));
            List<Map<String, String>> ans = new ArrayList<>();
            int k = 0;
            for (Map<String, Object> l : (List<Map<String, Object>>) ((Map) map.get("data")).get("list")) {
                String music_id = String.valueOf((l.get("rid")));
                String name = ((String)(l.get("name"))).replaceAll("&nbsp;", " ").replaceAll("&amp;", " ");
                if (name.indexOf('(') != -1) {
                    name = name.split("\\(")[0];
                }
                String artist = ((String)(l.get("artist"))).replaceAll("&amp;", " ").replaceAll("&nbsp;", " ");
                String finalName = name;
                ans.add(new HashMap<String, String>(){{
                    put("id", music_id);
                    put("name", finalName);
                    put("artist", artist);
                }});
                //保存music基本信息 缓存30分钟
                redisTemplate.opsForValue().set(SystemConstant.MUSIC_SIMPLY_INFO+music_id,
                        artist+"_"+finalName, SystemConstant.CACHE_TTL, TimeUnit.MINUTES);
                if (k++ > 7) {
                    break;
                }
            }
            return Result.ok(ans);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.fail("获取失败！");
    }

    /**
     * 获取单个歌曲的详细信息
     * @param id id
     */
    @Override
    public Result getMusicInfo(String id) {
        String specificInfo = redisTemplate.opsForValue().get(SystemConstant.MUSIC_SPECIFIC_INFO + id);
        if (specificInfo != null) {
            //更新保存时间
            redisTemplate.opsForValue().set(SystemConstant.MUSIC_SPECIFIC_INFO+id
                    , specificInfo);
            Music music = JSONUtil.toBean(specificInfo, Music.class);
            redisTemplate.opsForValue().set(SystemConstant.MUSIC_SIMPLY_INFO+id,
                    music.getMusicArtist()+"_"+music.getMusicName(), SystemConstant.CACHE_TTL, TimeUnit.HOURS);
            return Result.ok(music);
        }
        String reqId = redisTemplate.opsForValue().get(SystemConstant.REQ_ID);
        if (reqId == null) {
            log.error("获取reqid失败哦！");
            return Result.fail("获取失败！");
        }
        String url = "https://www.kuwo.cn/comment?type=get_rec_comment&f=web&page=1&rows=100&digest=15&sid="+ id +"&uid=0&prod=newWeb&httpsStatus=1&reqId=" + reqId;
        Connection conn = Jsoup.connect(url);
        conn.headers(headersMap);
        List<MusicComment> ans = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            try {
                Connection.Response demo = conn.ignoreContentType(true).method(Connection.Method.GET)
                        .execute();
                Document documentDemo = demo.parse();
                //这里就是获取该页面的HTML元素。
                String data = documentDemo.getElementsByTag("body").toString();
                Map<String, Object> map = JSONUtil.toBean(data.substring(7, data.length() - 7), Map.class);

                if (map.get("rows") != null) {
                    for (Map<String, Object> l : (List<Map<String, Object>>) (map.get("rows"))) {
                        String umsg = String.valueOf((l.get("msg")));
                        String uname = ((String)(l.get("u_name"))).replaceAll("&nbsp;", " ").replaceAll("&amp;", " ");
                        String upic = ((String)(l.get("u_pic")));
                        String ulike = ((String)(l.get("like_num")));
                        String time = ((String)(l.get("time")));
                        ans.add(new MusicComment(umsg, ulike, uname, upic, time));
                    }
                }
            } catch (Exception e) {
                log.error("未找到评论！");
            }
            if (ans.size() != 0) {
                break;
            }
        }

        Music music = new Music() {{
            setMusicComments(ans.toArray(new MusicComment[0]));
            setMusicId(id);
        }};
        for (int i = 0; i < 5; i++) {
            try {
                music.setTimeStamp(getMusicLrc(id));
            } catch (Exception e) {
                log.error("未找到歌词!");
            }
            if (music.getTimeStamp() != null) {
                break;
            }
        }
        String simplyInfo = redisTemplate.opsForValue().get(SystemConstant.MUSIC_SIMPLY_INFO+id);
        if (simplyInfo != null && !simplyInfo.isEmpty()) {
            music.setMusicName(simplyInfo.split("_")[1]);
            music.setMusicArtist(simplyInfo.split("_")[0]);
        } else if (music.getTimeStamp() != null && music.getTimeStamp().length != 0) {
            if (music.getTimeStamp()[0][1].contains("-")) {
                music.setMusicName(music.getTimeStamp()[0][1].split("-")[0]);
                music.setMusicArtist(music.getTimeStamp()[0][1].split("-")[1]);
            }
        }
        //保存redis 时间30小时
        redisTemplate.opsForValue().set(SystemConstant.MUSIC_SPECIFIC_INFO+id
                , JSONUtil.toJsonStr(music));
        return Result.ok(music);
    }

    /**
     * 获取歌曲url
     */
    @Override
    public Result getMusicUrl(String id) {
        String cache = redisTemplate.opsForValue().get(SystemConstant.MUSIC_URL + id);
        if (cache != null) {
            //更新保存时间
            redisTemplate.opsForValue().set(SystemConstant.MUSIC_URL+id
                    , cache,
                    SystemConstant.CACHE_NULL_TTL,
                    TimeUnit.MINUTES);
            return Result.ok(new MusicUrlVo(id, cache));
        }
        String reqId = redisTemplate.opsForValue().get(SystemConstant.REQ_ID);
        if (reqId == null) {
            log.error("获取reqid失败哦！");
            return Result.fail("获取失败！");
        }
        String url = "http://www.kuwo.cn/api/v1/www/music/playUrl?mid="+ id +"&type=convert_url3&httpsStatus=1&reqId=" + reqId;
        try {
            Connection conn = Jsoup.connect(url);
            Connection.Response demo = conn.ignoreContentType(true).method(Connection.Method.GET)
                    .execute();
            Document documentDemo = demo.parse();
            //这里就是获取该页面的HTML元素。
            String data = documentDemo.getElementsByTag("body").toString();
            String jsonStr = data.substring(7, data.length() - 7);
            String musicUrl = (String) ((Map) (JSONUtil.toBean(jsonStr, Map.class).get("data"))).get("url");
            redisTemplate.opsForValue().set(SystemConstant.MUSIC_URL+id
                    , musicUrl,
                    SystemConstant.CACHE_NULL_TTL,
                    TimeUnit.MINUTES);
            return Result.ok(new MusicUrlVo(id, musicUrl));
        } catch (Exception e) {
            log.error("获取url失败哦！");
            e.printStackTrace();
        }
        return Result.fail("获取url失败哦！");
    }

    /**
     * 获取音乐记录
     * @return List<MusicLogVo></>
     */
    @Override
    public Result getMusicLogs() {
        //redis 获取记录
        Set<ZSetOperations.TypedTuple<String>> logs = redisTemplate.opsForZSet().reverseRangeWithScores(SystemConstant.MUSIC_LOG, 0, -1);
        if (logs == null || logs.isEmpty()) {
            return Result.ok("暂无记录!");
        }
        List<MusicLogVo> list = new ArrayList<>();
        for (ZSetOperations.TypedTuple<String> tuple : logs) {
            String[] values = tuple.getValue().split("_");
            list.add(new MusicLogVo(values[2], values[1], values[0], tuple.getScore().intValue()));
        }
        return Result.ok(list);
    }

    /**
     * 更新音乐记录
     */
    @Override
    public Result updateMusicLog(String id) {
        if (!StringUtils.hasText(id) || "undefined".equals(id)) {
            return Result.fail("失败！");
        }
        //获取所有记录
        Set<String> logs = redisTemplate.opsForZSet().range(SystemConstant.MUSIC_LOG, 0, -1);
        String simplyInfo = redisTemplate.opsForValue().get(SystemConstant.MUSIC_SIMPLY_INFO+id);
        //log 为空
        if (logs == null || logs.isEmpty()) {
            if (StringUtils.hasText(simplyInfo)) {
                String key = simplyInfo + "_" + id;
                redisTemplate.opsForZSet().add(SystemConstant.MUSIC_LOG, key, 0L);
                redisTemplate.opsForZSet().incrementScore(SystemConstant.MUSIC_LOG, key, 1);
                return Result.ok();
            }
            return Result.fail("更新歌曲记录失败!");
        }
        List<String> collect = logs.stream().filter(i -> i.split("_")[2].equals(id)).collect(Collectors.toList());
        if (collect.isEmpty()) {
            if (StringUtils.hasText(simplyInfo)) {
                String key = simplyInfo + "_" + id;
                redisTemplate.opsForZSet().add(SystemConstant.MUSIC_LOG, key, 0L);
                redisTemplate.opsForZSet().incrementScore(SystemConstant.MUSIC_LOG, key, 1);
                return Result.ok();
            }
            return Result.fail("更新歌曲记录失败!");
        }
        //更新redis
        redisTemplate.opsForZSet().incrementScore(SystemConstant.MUSIC_LOG, collect.get(0), 1);
        redisTemplate.opsForValue().set(SystemConstant.MUSIC_SIMPLY_INFO+id, collect.get(0), SystemConstant.CACHE_TTL, TimeUnit.HOURS);
        return Result.ok();
    }

    /**
     * 根据id在redis_log中查找
     * @param id musicId
     */
    public String queryLogKeyById(String id) {
        String simplyInfo = redisTemplate.opsForValue().get(SystemConstant.MUSIC_SIMPLY_INFO+id);
        String key = "";
        if (StringUtils.hasText(simplyInfo)) {
            key = simplyInfo + "_" + id;
        } else {
            Set<String> logs = redisTemplate.opsForZSet().range(SystemConstant.MUSIC_LOG, 0, -1);
            if (logs == null || logs.isEmpty()) {
                return key;
            }
            List<String> collect = logs.stream().filter(i -> i.split("_")[2].equals(id)).collect(Collectors.toList());
            if (collect.isEmpty()) {
                return key;
            }
            key = collect.get(0);
        }
        return key;
    }

    /**
     * 删除音乐记录
     */
    @Override
    public Result deleteMusicLog(String id, String musicName, String musicArtist) {
        String simplyInfo;
        if (StringUtils.hasText(musicName) && StringUtils.hasText(musicArtist)) {
            simplyInfo = musicArtist + "_" + musicName;
        } else {
            simplyInfo = redisTemplate.opsForValue().get(SystemConstant.MUSIC_SIMPLY_INFO+id);
        }
        if (StringUtils.hasText(simplyInfo)) {
            String key = simplyInfo + "_" + id;
            redisTemplate.opsForZSet().remove(SystemConstant.MUSIC_LOG, key);
            //基本信息
//            redisTemplate.delete(SystemConstant.MUSIC_SIMPLY_INFO+id);
            //详细信息
//            redisTemplate.delete(SystemConstant.MUSIC_SPECIFIC_INFO+id);
            //url
//            redisTemplate.delete(SystemConstant.MUSIC_URL+id);
            return Result.ok();
        }
        return Result.fail("删除失败！");
    }

    /**
     * 获取下一首url
     */
    @Override
    public Result getNextMusicUrl(String curId) {
        if (!StringUtils.hasText(curId) || "undefined".equals(curId)) {
            return Result.fail("失败！");
        }
        //获取所有log
        Set<ZSetOperations.TypedTuple<String>> logs = redisTemplate.opsForZSet().reverseRangeWithScores(SystemConstant.MUSIC_LOG, 0, -1);
        if (logs == null || logs.isEmpty()) {
            return Result.ok("暂无记录!");
        }
        //当前id在log中对应的key
        String curKey = queryLogKeyById(curId);
        //redis 获取记录
        boolean next = false;
        String nextId = "";
        String firstId = "";
        //查找nextId
        for (ZSetOperations.TypedTuple<String> tuple : logs) {
            String key = tuple.getValue();
            if (key == null) {
                continue;
            }
            String[] t = key.split("_");
            String id = t[t.length - 1];
            if ("".equals(firstId)) {
                firstId = id;
            }
            if (next) {
                nextId = id;
                break;
            }
            if (key.equals(curKey)) {
                next = true;
            }
        }
        if (next) {
            if ("".equals(nextId)) {
                return getMusicUrl(firstId);
            }
            return getMusicUrl(nextId);
        } else {
            return Result.fail("获取下一首失败!");
        }
    }

    /**
     * 查找歌词
     */
    public String[][] getMusicLrc(String id) throws IOException {
        String urlLrc = "http://m.kuwo.cn/newh5/singles/songinfoandlrc?musicId="+id;
        Connection conn = Jsoup.connect(urlLrc);
        //模拟浏览器的请求头
        conn.headers(headersMap);
        //开始连接HTTP请求。xxxx
        Connection.Response demoLrc = conn.ignoreContentType(true).method(Connection.Method.GET)
                .execute();
        Document documentDemo = demoLrc.parse();
        String data = documentDemo.getElementsByTag("body").toString();
        Map<String, Object> map = JSONUtil.toBean(data.substring(7, data.length() - 7), Map.class);
        List<String[]> timeStamp = new ArrayList<>();
        for (Map<String, Object> l : (List<Map<String, Object>>) ((Map) map.get("data")).get("lrclist")) {
            String lineLyric = ((String)(l.get("lineLyric")));
            String time = String.valueOf(l.get("time"));
            timeStamp.add(new String[]{time, lineLyric});
        }
        return timeStamp.toArray(new String[0][]);
    }
}
